package com.quick.develop.flowable.schedule;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.map.MapUtil;
import com.quick.develop.flowable.constant.BpmConstants;
import com.quick.develop.framework.utils.DateUtils;
import com.quick.develop.framework.utils.StringUtils;
import com.quick.develop.flowable.domain.entrust.BpmEntrustDueReqVO;
import com.quick.develop.flowable.domain.entrust.BpmEntrustEx;
import com.quick.develop.flowable.framework.factory.FlowServiceFactory;
import com.quick.develop.flowable.schedule.core.BpmScheduleHandler;
import com.quick.develop.flowable.service.definition.IBpmProcessDefinitionService;
import com.quick.develop.flowable.service.process.IBpmEntrustExService;
import lombok.extern.slf4j.Slf4j;
import org.flowable.engine.repository.ProcessDefinition;
import org.flowable.task.api.Task;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.*;

import static com.quick.develop.framework.utils.collection.CollectionUtils.*;

/**
 * @author junyuan.liu
 * @description:
 * @date 2022/9/8 9:33
 */
@Slf4j
@Component
public class BpmResolveEntrustTask extends FlowServiceFactory implements BpmScheduleHandler {

    @Resource
    private IBpmProcessDefinitionService bpmProcessDefinitionService;

    @Resource
    private IBpmEntrustExService bpmEntrustExService;

    @Value("${flowable.custom.entrust.trace-source}")
    private boolean traceSource;

    // @Value("${flowable.custom.entrust.release-single}")
    // private boolean releaseSingle;

    /**
     * @description 异步执行 免得超时
     * @author junyuan.liu
     * @date 2022/9/12 8:54
     */
    @Override
    @Async
    @Transactional(rollbackFor = Exception.class)
    public void execute() {
        List<Task> list = taskService.createTaskQuery()
                .taskDescriptionLikeIgnoreCase("%" + BpmConstants.AUTO_ENTRUST + "%")
                .includeTaskLocalVariables()
                .list();
        if (CollUtil.isNotEmpty(list)) {
            releaseSingle(list);
            /*if (releaseSingle) {
                releaseSingle(list);
            }else {
                releaseBatch(list);
            }*/
        }
    }

    private void releaseSingle(List<Task> list) {
        Map<String, ProcessDefinition> definitionMap = bpmProcessDefinitionService.getProcessDefinitionMap(convertSet(list, Task::getProcessDefinitionId));
        HashSet<Long> delIds = new HashSet<>();
        list.forEach(task -> {
            try {
                String definitionId = task.getProcessDefinitionId();
                if (StringUtils.isNotEmpty(task.getAssignee()) && definitionMap.containsKey(definitionId)) {
                    BpmEntrustEx entrustEx = getEntrustEx(task, definitionMap.get(definitionId));
                    Optional.ofNullable(entrustEx).ifPresent(ex -> {
                        if (!Objects.equals(ex.getCreatedUserCode(), task.getAssignee())) {
                            taskService.setVariableLocal(task.getId(), BpmConstants.ENTRUST_RELEASE, BpmConstants.ENTRUST_RELEASE);
                            taskService.setAssignee(task.getId(), ex.getCreatedUserCode());
                            delIds.add(entrustEx.getId());
                            // this.delEntrust(entrustEx.getId());
                        }
                    });
                }
            } catch (Exception e) {
                log.error("任务:{}；委托释放失败：{}", task.getId(), e.getMessage());
                e.printStackTrace();
            }
        });
        if (!delIds.isEmpty()) {
            this.delEntrust(delIds.toArray(new Long[0]));
        }
    }


    private BpmEntrustEx getEntrustEx(Task task, ProcessDefinition definition) {
        try {
            String user = traceSource ? getSourceUser(task) : task.getAssignee();
            BpmEntrustDueReqVO reqVO = new BpmEntrustDueReqVO();
            reqVO.setEntrustUser(user);
            reqVO.setModelKey(definition.getKey());
            reqVO.setProcessInstanceId(task.getProcessInstanceId());
            reqVO.setDueDate(DateUtils.dateTime(DateUtils.YYYY_MM_DD, DateUtils.getDate()));
            return bpmEntrustExService.getOverdueEntrust(reqVO);
        } catch (Exception e) {
            log.error("单条委托过期数据查询异常：{}", e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    private void releaseBatch(List<Task> list) {
        List<ProcessDefinition> defList = bpmProcessDefinitionService.getProcessDefinitionListByDefinitionIds(convertSet(list, Task::getProcessDefinitionId));
        Map<String, ProcessDefinition> definitionMap = convertMap(defList, ProcessDefinition::getId);
        List<BpmEntrustEx> dueList = getEntrustExList(list, defList);
        list.forEach(task -> {
            try {
                String definitionId = task.getProcessDefinitionId();
                if (StringUtils.isNotEmpty(task.getAssignee()) && definitionMap.containsKey(definitionId)) {
                    BpmEntrustEx entrustEx = findFirst(dueList, d -> Objects.equals(d.getModelKey(), definitionMap.get(definitionId).getKey()) && Objects.equals(d.getEntrustToUser(), task.getAssignee()));
                    Optional.ofNullable(entrustEx).ifPresent(ex -> {
                        if (!Objects.equals(ex.getCreatedUserCode(), task.getAssignee())) {
                            taskService.setVariableLocal(task.getId(), BpmConstants.ENTRUST_RELEASE, BpmConstants.ENTRUST_RELEASE);
                            taskService.setAssignee(task.getId(), ex.getCreatedUserCode());
                            // this.delEntrust(entrustEx.getId());
                        }
                    });
                }
            } catch (Exception e) {
                log.error("任务:{}；委托释放失败：{}", task.getId(), e.getMessage());
                e.printStackTrace();
            }
        });
    }

    private List<BpmEntrustEx> getEntrustExList(List<Task> list, List<ProcessDefinition> defList) {
        try {
            Set<String> entrustUsers = traceSource ? getSourceUsers(list) : convertSet(list, Task::getAssignee);
            BpmEntrustDueReqVO reqVO = new BpmEntrustDueReqVO();
            reqVO.setModelKeys(convertSet(defList, ProcessDefinition::getKey));
            reqVO.setEntrustUsers(entrustUsers);
            reqVO.setDueDate(DateUtils.dateTime(DateUtils.YYYY_MM_DD, DateUtils.getDate()));
            return bpmEntrustExService.getOverdueEntrustList(reqVO);
        } catch (Exception e) {
            log.error("批量委托过期数据查询异常：{}", e.getMessage());
            e.printStackTrace();
        }
        return null;
    }

    private String getSourceUser(Task task) {
        String assignee = task.getAssignee();
        Map<String, Object> var = task.getTaskLocalVariables();
        return Optional.of(MapUtil.getStr(var, BpmConstants.ENTRUST_ASSIGNEE))
                .filter(u -> !Objects.equals(assignee, u))
                .orElse(assignee);
    }

    private Set<String> getSourceUsers(List<Task> list) {
        return convertSet(list, task -> {
            Map<String, Object> var = task.getTaskLocalVariables();
            String assignee = task.getAssignee();
            return Optional.of(MapUtil.getStr(var, BpmConstants.ENTRUST_ASSIGNEE))
                    .filter(u -> !Objects.equals(assignee, u))
                    .orElse(assignee);
        });
    }

    private void delEntrust(Long[] ids) {
        try {
            bpmEntrustExService.deleteEntrust(ids);
        } catch (Exception e) {
            log.error("委托：{}，释放后，删除委托数据失败：{}", StringUtils.join(ids, ","), e.getMessage());
            e.printStackTrace();
        }
    }
}
